//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler
// FILE:            expr.cpp - expression parser
// AUTHOR:          Vladimir Gumenuk
// DATE:            27-Mar-2008
//
// MODIFIED:        26-Mar-2008 - old file was taken as the base for xort 
//                         expression parser
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"
#include "listImpl.h"

// forward
STATIC int exp_level1_conditional( SYMBOLLIST symList, SymbolRef vResult, SymbolRef vTargetResult, int bInCondExp, SymbolRef Func );

STATIC Token        g_Token;
STATIC int          g_bTokenGot = FALSE;
STATIC dword        g_dwDotStart = 0;

//---------------------------------------------------------------------------------------------
// get next token from input line
//---------------------------------------------------------------------------------------------
STATIC int exp_get_token( SYMBOLLIST symList )
{
    int iRet = XORT_OK;

    g_Token = lex_GetToken( symList );
    g_bTokenGot = TRUE;

    if( g_Token.m_TT == tt_unknown )
        iRet = ERR_SYNTAX;

    return iRet;
}
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
STATIC int exp_call( SYMBOLLIST symList, SymbolRef vResult, SymbolRef ProcToCall, SymbolRef Object, SymbolRef procOwner )
{
    int iRet = do_callproc( symList, procOwner, ProcToCall, TRUE, Object );
    if( iRet != XORT_OK ) {
        return iRet;
    }

    if( !g_bTokenGot ) {
        g_Token = lex_GetLastToken();
        g_bTokenGot = TRUE;
    }

    ProcToCall.Copy( vResult );
    return XORT_OK;
}
//---------------------------------------------------------------------------------------------
// gets primary value
//---------------------------------------------------------------------------------------------
STATIC int exp_primary( SYMBOLLIST symList, SymbolRef vResult, SymbolRef pObject, SymbolRef Func )
{
    int iRet = XORT_OK;

    if( g_Token.m_TT == tt_ident )
    {
        Symbol Var;
        if( lex_ConfirmMember( symList, Var, pObject ) == 1 ) { // if object, 1 - is the only valid return
            XortElement x; x.bType = t_u2;
            x._u2 = Var.m_iIndex;
            Var.Copy( vResult );
            iRet = gen_loadConst( Func, x );
        } else {
            SymbolRef Sym = FindSymbol( symList, g_Token.m_csVal, FALSE );
            if( Sym.isNULL() ) {
                x_error( ERR_IDENT_NOT_FOUND, g_Token.m_dwLine, g_Token.m_dwPos );
                return ERR_SYNTAX;
            }

            if( Sym.m_SymKind == sk_var || Sym.m_SymKind == sk_arg ) { // variable
                Sym.Copy( vResult );
                if( !Func.isNULL() ) {
                    g_dwDotStart = Func.m_Code.count();
                    if( Sym.m_SymKind == sk_var && Sym.m_dwFlags & f_const ) {
                        if( Sym.m_SymKind == sk_var && Sym.m_dwType >= t_p ) { // constant object variables support
                            iRet = gen_loadConstVar( Func, Sym );
                        } else { // else load numeric constant
                            iRet = gen_loadConst( Func, vResult.m_Value ); 
                        }
                    } else {
                        iRet = gen_load( Func, vResult ); // load normal stack or memory variable to the stack
                    }
                }
            } else if( Sym.m_SymKind == sk_proc ) {// call function
                iRet = exp_call( symList, vResult, Sym, getNULLSymbol(), Func );
            } else {
                x_error( ERR_IDENT_ISVARORPROC, g_Token.m_dwLine, g_Token.m_dwPos );
                return ERR_SYNTAX;
            }
        }
    }
    else if( g_Token.m_TT == tt_digit )
    {
        if( g_Token.m_dwTokenCode == tk_number ) {
            vResult.m_SymKind = sk_integer;
        } else if( g_Token.m_dwTokenCode == tk_realnumber ) {
            vResult.m_SymKind = sk_real;
        } else {
            return ERR_INTERNAL;
        }

        vResult.m_dwFlags = f_const;
        vResult.m_dwType  = g_Token.m_Val.bType;
        x_copyelement( vResult.m_Value, g_Token.m_Val );
        if( !Func.isNULL() ) {
            gen_loadConst( Func, vResult.m_Value );
        }
    }
    else if( g_Token.m_TT == tt_literal )
    {   // In the very old version of the compiler strings were treated as indexes in literal pool
        // Now this needs to be changed to pointers to string object
        SymbolRef strVar = AddStringVarToGlobalScope( g_Token.m_dwLiteralIdx );
        strVar.Copy( vResult );
        if( !Func.isNULL() ) {
            gen_loadConstVar( Func, vResult );
        }
    }
    else if( g_Token.m_TT == tt_null ) // special case NULL ptr assignment
    {
        vResult.m_SymKind      = sk_nullptr;
        vResult.m_dwFlags      = f_const; // NULL is treated like special constant for pointers
        vResult.m_dwType       = t_p;
        vResult.m_Value.bType  = t_p;
        if( !Func.isNULL() ) {
            gen_load( Func, vResult );
        }
    } else {
        iRet = ERR_SYNTAX;
    }

    if( iRet == XORT_OK ) {       // if token can be evaluated in constant expression
        iRet = exp_get_token( symList );    // get next token
    }

    return iRet;
}
// r = -r;
STATIC int const_neg( XELEMENT x ) 
{
    if( x.bType == t_s1 ) {        x._s1 = -x._s1;
    } else if( x.bType == t_u1 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_s2 ) { x._s2 = -x._s2;
    } else if( x.bType == t_u2 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_s4 ) { x._s4 = -x._s4;
    } else if( x.bType == t_u4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_s8 ) { x._s8 = -x._s8;
    } else if( x.bType == t_u8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r4 ) { x._r4 = -x._r4;
    } else if( x.bType == t_r8 ) { x._r8 = -x._r8;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    if( x.bType == t_u1 || x.bType == t_u2 || x.bType == t_u4 || x.bType == t_u8 ) {
        x_error( ERR_UNARY_MINUS_APPLIED_TOUNSIGNED, g_Token.m_dwLine, g_Token.m_dwPos );
    }
    return XORT_OK;
}
// r = ~r;
STATIC int const_not( XELEMENT x )
{
    if( x.bType == t_s1 ) {        x._s1 = ~x._s1;
    } else if( x.bType == t_u1 ) { x._u1 = ~x._u1;
    } else if( x.bType == t_s2 ) { x._s2 = ~x._s2;
    } else if( x.bType == t_u2 ) { x._u2 = ~x._u2;
    } else if( x.bType == t_s4 ) { x._s4 = ~x._s4;
    } else if( x.bType == t_u4 ) { x._u4 = ~x._u4;
    } else if( x.bType == t_s8 ) { x._s8 = ~x._s8;
    } else if( x.bType == t_u8 ) { x._u8 = ~x._u8;
    } else if( x.bType == t_r4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}
// r = !r;
STATIC int const_lognot( XELEMENT x )
{
    if( x.bType == t_s1 ) {        x._s1 = !x._s1;
    } else if( x.bType == t_u1 ) { x._u1 = !x._u1;
    } else if( x.bType == t_s2 ) { x._s2 = !x._s2;
    } else if( x.bType == t_u2 ) { x._u2 = !x._u2;
    } else if( x.bType == t_s4 ) { x._s4 = !x._s4;
    } else if( x.bType == t_u4 ) { x._u4 = !x._u4;
    } else if( x.bType == t_s8 ) { x._s8 = !x._s8;
    } else if( x.bType == t_u8 ) { x._u8 = !x._u8;
    } else if( x.bType == t_r4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int exp_const_unary( dword op, SymbolRef vResult )
{
    int iRet = XORT_OK;

    if( vResult.m_dwFlags != f_const ) {
        return ERR_INTERNAL;
    }
    if( op == tk_plus ) { // nothing to do
        return iRet;
    } else if( op == tk_minus ) { // r = -r;
        iRet = const_neg( vResult.m_Value );
    } else if( op == tk_tilda ) { // r = ~r;
        iRet = const_not( vResult.m_Value );
    } else if( op == tk_not ) {   // r = !r;
        iRet = const_lognot( vResult.m_Value );
    } else {
        iRet = ERR_SYNTAX;
    }

    return XORT_OK;
}
//---------------------------------------------------------------------------------------------
// make unary operation
//---------------------------------------------------------------------------------------------
STATIC int exp_unary( dword op, SymbolRef vResult, SymbolRef Func )
{ 
    int iRet;
    iRet = XORT_OK;

    if( Func.isNULL() ) {
        return exp_const_unary( op, vResult );
    }

    if( op == tk_plus ) { // nothing to do
        return iRet;
    } else if( op == tk_minus ) { // r = -r;
        iRet = gen_neg( Func, vResult );
    } else if( op == tk_tilda ) { // r = ~r;
        iRet = gen_not( Func, vResult );
    } else if( op == tk_not ) {   // r = !r;
        iRet = gen_lognot( Func, vResult );
    } else if( op == tk_inc )  {
        iRet = gen_inc_dec_onStackTop( p_inc, Func, vResult );
        if( iRet == XORT_OK ) { // save result into variable and leave result on stack to continue expr evaluation
            gen_dup( Func );    // this will not handle some complex case like ++( A * B )... needs to be improved
            gen_assign( Func, vResult, vResult );
        }
    } else if( op == tk_dec )  {
        iRet = gen_inc_dec_onStackTop( p_dec, Func, vResult );
        if( iRet == XORT_OK ) { // save result into variable and leave result on stack to continue expr evaluation
            gen_dup( Func );
            gen_assign( Func, vResult, vResult );
        }
    } else {
        iRet = ERR_SYNTAX;
    }

    return iRet;
}

int const_binor( XELEMENT x, XELEMENT y ) // |
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 |= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 |= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 |= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 |= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 |= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 |= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 |= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 |= z._u8;
    } else if( x.bType == t_r4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_xor( XELEMENT x, XELEMENT y ) // ^
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 ^= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 ^= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 ^= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 ^= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 ^= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 ^= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 ^= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 ^= z._u8;
    } else if( x.bType == t_r4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_binand( XELEMENT x, XELEMENT y ) // &
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 &= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 &= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 &= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 &= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 &= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 &= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 &= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 &= z._u8;
    } else if( x.bType == t_r4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_shiftleft( XELEMENT x, XELEMENT y ) // <<
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 <<= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 <<= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 <<= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 <<= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 <<= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 <<= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 <<= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 <<= z._u8;
    } else if( x.bType == t_r4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_shiftright( XELEMENT x, XELEMENT y ) // >>
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 >>= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 >>= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 >>= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 >>= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 >>= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 >>= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 >>= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 >>= z._u8;
    } else if( x.bType == t_r4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_plus( XELEMENT x, XELEMENT y )      // +
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 += z._s1;
    } else if( x.bType == t_u1 ) { x._u1 += z._u1;
    } else if( x.bType == t_s2 ) { x._s2 += z._s2;
    } else if( x.bType == t_u2 ) { x._u2 += z._u2;
    } else if( x.bType == t_s4 ) { x._s4 += z._s4;
    } else if( x.bType == t_u4 ) { x._u4 += z._u4;
    } else if( x.bType == t_s8 ) { x._s8 += z._s8;
    } else if( x.bType == t_u8 ) { x._u8 += z._u8;
    } else if( x.bType == t_r4 ) { x._r4 += z._r4;
    } else if( x.bType == t_r8 ) { x._r8 += z._r8;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_minus( XELEMENT x, XELEMENT y )     // -
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 -= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 -= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 -= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 -= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 -= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 -= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 -= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 -= z._u8;
    } else if( x.bType == t_r4 ) { x._r4 -= z._r4;
    } else if( x.bType == t_r8 ) { x._r8 -= z._r8;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_mul( XELEMENT x, XELEMENT y )       // *
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 *= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 *= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 *= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 *= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 *= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 *= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 *= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 *= z._u8;
    } else if( x.bType == t_r4 ) { x._r4 *= z._r4;
    } else if( x.bType == t_r8 ) { x._r8 *= z._r8;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_div( XELEMENT x, XELEMENT y )      // /
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 /= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 /= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 /= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 /= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 /= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 /= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 /= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 /= z._u8;
    } else if( x.bType == t_r4 ) { x._r4 /= z._r4;
    } else if( x.bType == t_r8 ) { x._r8 /= z._r8;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_mod( XELEMENT x, XELEMENT y )      // %
{
    XortElement z;
    int iRet = x_convert_type( x.bType, z, y );
    if( iRet != XORT_OK ) {
        return iRet;
    }
    if( x.bType == t_s1 ) {        x._s1 %= z._s1;
    } else if( x.bType == t_u1 ) { x._u1 %= z._u1;
    } else if( x.bType == t_s2 ) { x._s2 %= z._s2;
    } else if( x.bType == t_u2 ) { x._u2 %= z._u2;
    } else if( x.bType == t_s4 ) { x._s4 %= z._s4;
    } else if( x.bType == t_u4 ) { x._u4 %= z._u4;
    } else if( x.bType == t_s8 ) { x._s8 %= z._s8;
    } else if( x.bType == t_u8 ) { x._u8 %= z._u8;
    } else if( x.bType == t_r4 ) { return ERR_INVALID_TYPE;
    } else if( x.bType == t_r8 ) { return ERR_INVALID_TYPE;
    } else if( x.bType >= t_p  ) { return ERR_INVALID_TYPE;
    }
    return XORT_OK;
}

int const_arith( dword op, XELEMENT x, XELEMENT y )
{
    int iRet;

    if( op == tk_binor ) {     // |
        iRet = const_binor( x, y );
    } else if( op == tk_xor ) {       // ^
        iRet = const_xor( x, y );
    } else if( op == tk_binand ) {    // &
        iRet = const_binand( x, y );
    } else if( op == tk_shiftleft ) { // <<
        iRet = const_shiftleft( x, y );
    } else if( op == tk_shiftright ) { // >>
        iRet = const_shiftright( x, y );
    } else if( op == tk_plus ) {      // +
        iRet = const_plus( x, y );
    } else if( op == tk_minus ) {     // -
        iRet = const_minus( x, y );
    } else if( op == tk_mul ) {       // *
        iRet = const_mul( x, y );
    } else if( op == tk_div ) {      // /
        iRet = const_div( x, y );
    } else if( op == tk_mod ) {       // %
        iRet = const_mod( x, y );
    } else {
        return ERR_OPERATION_NOT_ALLOWED;
    }
    
    return iRet;
}

STATIC int exp_const_arith( dword op, SymbolRef pResult, SymbolRef pHold, SymbolRef pHold2 )
{
    int iRet;

    if( pResult.m_dwFlags != f_const || pHold.m_dwFlags != f_const ) {
        return ERR_INTERNAL;
    }

    iRet = const_arith( op, pResult.m_Value, pHold.m_Value );
    if( iRet != XORT_OK )
    {
        x_error( iRet, g_Token.m_dwLine, g_Token.m_dwPos );
        x_error( ERR_CONST_EXPR_SYNTAX, g_Token.m_dwLine, g_Token.m_dwPos );
        iRet = ERR_SYNTAX;
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// calculates expression int
//---------------------------------------------------------------------------------------------
STATIC int exp_arith( SYMBOLLIST symList, dword op, SymbolRef pResult, SymbolRef pHold, SymbolRef pHold2, dword dwCodeStart, SymbolRef Func )
{
    int iRet = XORT_OK;
    Symbol Result;
    Result.Zero();
    
    if( Func.isNULL() ) {
        return exp_const_arith( op, pResult, pHold, pHold2 );
    }

    if( op == tk_quest ) {      // ? :
        iRet = XORT_OK;  // nothing to do here, everything is done on the upper level, just update result
        Result.m_SymKind = pHold.m_SymKind;
        Result.m_dwType  = pHold.m_dwType;
        Result.m_dwFlags = pHold.m_dwFlags;
        // small verification
        if( pHold.m_dwType != pHold2.m_dwType ) {
            message( "TRENARY: IS HERE CONVERSION NEEDED?" ); // for future
        }
    } else if( op == tk_ptr ) {
        if( pHold.m_SymKind == sk_proc ) {
            INSTRUCTION_ARRAY arCode;

            dword dwCodeSize = Func.m_Code.count();
            dword dwStart = dwCodeStart;
            while( dwStart < dwCodeSize ) { // copy the code that gets method ptr to put arguments on the stack first!
                arCode.add( Func.m_Code.get( dwStart ) );
                ++dwStart;
            }

            while( Func.m_Code.count() != dwCodeStart ) { // delete generated code
                Func.m_Code.del();
            }

            // this gets arguments to the stack
            lex_PutBack(); // need this, because expression analyzer eats '('
            iRet = exp_call( symList, pResult, pHold, pHold, Func ); // third pHold - just for test, but seems works fine :)
            if( iRet != XORT_OK )
                return iRet;

            dwStart = 0;
            while( dwStart < arCode.count() ) { // now copy the stored code back
                Func.m_Code.add( arCode.get( dwStart ) );
                ++dwStart;
            }

            iRet = gen_invoke( Func, pHold );
            if( iRet == XORT_OK ) {
                word dwNumArgs = 0;
                if( !(pHold.m_dwFlags & f_unspecnumargs) ) { // try to fill the number of arguments
                    dwNumArgs = pHold.m_dwArgumentsCount;
                }
                gen_EndCall( Func, dwNumArgs );
                
                if( pHold.m_pOwner && isObjectImported( DEREF(pHold.m_pOwner) ) && pHold.m_dwType >= t_p && pHold.m_dwType <= t_system ) { // only for this case!!!
                    iRet = gen_unpack( Func, pHold );
                    if( iRet != XORT_OK )
                        return iRet;
                }
                
                iRet = exp_get_token( symList );
            }
            g_dwDotStart = 0;
        } else {
            iRet = gen_member_get( Func, pResult, pHold );
        }
    } else if( op == tk_logor ) {        // ||
        iRet = gen_logic_arith( p_lor, Func, pResult, pHold, Result );
    } else if( op == tk_logand ) {       // &&
        iRet = gen_logic_arith( p_land, Func, pResult, pHold, Result );
    } else if( op == tk_binor ) {     // |
        iRet = gen_arith( p_or, Func, pResult, pHold ); // TODO: add resulting output type!!!
    } else if( op == tk_xor ) {       // ^
        iRet = gen_arith( p_xor, Func, pResult, pHold );
    } else if( op == tk_binand ) {    // &
        iRet = gen_arith( p_and, Func, pResult, pHold );
    } else if( op == tk_equal ) {    // ==
        iRet = gen_cmp( p_cmpeq, Func, pResult, pHold );
    } else if( op == tk_notequal ) {  // !=
        iRet = gen_cmp( p_cmpneq, Func, pResult, pHold );
    } else if( op == tk_lessequal ) { // <=
        iRet = gen_cmp( p_cmple, Func, pResult, pHold );
    } else if( op == tk_greaterequal ) { // >=
        iRet = gen_cmp( p_cmpge, Func, pResult, pHold );
    } else if( op == tk_less ) {     // <
        iRet = gen_cmp( p_cmplt, Func, pResult, pHold );
    } else if( op == tk_greater ) {  // >
        iRet = gen_cmp( p_cmpgt, Func, pResult, pHold );
    } else if( op == tk_shiftleft ) { // <<
        iRet = gen_arith( p_shl, Func, pResult, pHold );
    } else if( op == tk_shiftright ) { // >>
        iRet = gen_arith( p_shr, Func, pResult, pHold );
    } else if( op == tk_plus ) {      // +
        iRet = gen_arith( p_add, Func, pResult, pHold );
    } else if( op == tk_minus ) {     // -
        iRet = gen_arith( p_sub, Func, pResult, pHold );
    } else if( op == tk_mul ) {       // *
        iRet = gen_arith( p_mul, Func, pResult, pHold );
    } else if( op == tk_div ) {      // /
        iRet = gen_arith( p_div, Func, pResult, pHold );
    } else if( op == tk_mod ) {       // %
        iRet = gen_arith( p_mod, Func, pResult, pHold );
    } else {
        iRet = ERR_SYNTAX;
    }

    if( iRet != XORT_OK ) {
        x_error( ERR_EXPR_SYNTAX, g_Token.m_dwLine, g_Token.m_dwPos );
    } else {
        if( !Result.isNULL() ) {
            Result.Copy( pResult ); // very important - the arith can change the type of the expression due to internal type conversion!
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// () parenthesis
//---------------------------------------------------------------------------------------------
STATIC int exp_level14_parenthesis( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet;

    if( g_Token.m_dwTokenCode == tk_parenthopen )
    {
        iRet = exp_get_token( symList );
        if( iRet == XORT_OK )
        {
            iRet = exp_level1_conditional(symList, vResult, getNULLSymbol(), bInCondExp, Func);
            if( iRet == XORT_OK ) // level0 is removed
            {
                if( g_Token.m_dwTokenCode != tk_parenthclose ) {
                    iRet = ERR_SYNTAX;
                } else {
                    iRet = exp_get_token( symList );
                }
            }
        }
    } else {
        iRet = exp_primary( symList, vResult, getNULLSymbol(), Func );
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// . member pointer
//---------------------------------------------------------------------------------------------
STATIC int exp_level13_memptr( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;
    dword dwCodeStart = 0;
    
    iRet = exp_level14_parenthesis( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_ptr )
        {
            if( dwCodeStart == 0 ) {
                dwCodeStart = g_dwDotStart;
            }
            
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_primary( symList, vHold, vResult, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), dwCodeStart, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                        vHold.Copy( vResult );
                        vHold.Zero();
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// + - ~ ! Unary
//---------------------------------------------------------------------------------------------
STATIC int exp_level12_unary( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold;
    int bUnary = FALSE;
    dword op = g_Token.m_dwTokenCode;

    if( g_Token.m_TT == tt_delim && (op == tk_plus || op == tk_minus || op == tk_tilda || op == tk_not || op == tk_inc || op == tk_dec) )
    {
        bUnary = TRUE;
        iRet = exp_get_token( symList );
        if( iRet != XORT_OK ) {
            return iRet;
        }
    }

    iRet = exp_level13_memptr( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        if( bUnary ) {
            iRet = exp_unary( op, vResult, Func );
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// *  /  % Multiplicative 
//---------------------------------------------------------------------------------------------
STATIC int exp_level11_mult_div_mod( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level12_unary( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_mul || op == tk_div || op == tk_mod )
        {
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level12_unary( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// +  - Additive 
//---------------------------------------------------------------------------------------------
STATIC int exp_level10_add_sub( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level11_mult_div_mod( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_plus || op == tk_minus )
        {
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level11_mult_div_mod( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// << >> Bitwise shift 
//---------------------------------------------------------------------------------------------
STATIC int exp_level9_bitwise_shift( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level10_add_sub( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_shiftleft || op == tk_shiftright )
        {
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level10_add_sub( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// < > <=  >= Relational 
//---------------------------------------------------------------------------------------------
STATIC int exp_level8_logical_cmp( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level9_bitwise_shift( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_lessequal || op == tk_greaterequal || op == tk_less || op == tk_greater )
        {
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level9_bitwise_shift( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// ==  !=   Equality 
//---------------------------------------------------------------------------------------------
STATIC int exp_level7_logical_equal( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level8_logical_cmp( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_equal || op == tk_notequal )
        {
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level8_logical_cmp( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// & Bitwise-AND
//---------------------------------------------------------------------------------------------
STATIC int exp_level6_bitwise_and( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level7_logical_equal( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_binand )
        {
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level7_logical_equal( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// ^ Bitwise-exclusive-OR
//---------------------------------------------------------------------------------------------
STATIC int exp_level5_bitwise_excl_or( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level6_bitwise_and( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_xor )
        {
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level6_bitwise_and( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// | Bitwise-inclusive-OR
//---------------------------------------------------------------------------------------------
STATIC int exp_level4_bitwise_incl_or( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level5_bitwise_excl_or( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_binor )
        {
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level5_bitwise_excl_or( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// && logical AND
//---------------------------------------------------------------------------------------------
STATIC int exp_level3_logical_and( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level4_bitwise_incl_or( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_logand )
        {
            word wContinueLbl = 0;

            // gen test of first part of some logical expression and jump to wSkipLabel in case false
            // typical case will be: if( obj && obj.name ... )
            if( bInCondExp ) // means that we are called from condition analyzer
            {
                wContinueLbl = get_NextLabel();
                gen_nz( Func, vResult ); // convert result to boolean -> t_s4
                gen_dup( Func );
                gen_ifz( Func, vResult, wContinueLbl ); // in case of zero, we live the result on stack, so it can be used in subsequent calculations!
            }

            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level4_bitwise_incl_or( symList, vHold, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;
                    }
                } else
                    break;

                if( wContinueLbl ) {
                    gen_label( Func, wContinueLbl ); // continue expression evaluation at this point
                }

            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// || logical OR
//---------------------------------------------------------------------------------------------
STATIC int exp_level2_logical_or( SYMBOLLIST symList, SymbolRef vResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold; dword op;

    iRet = exp_level3_logical_and( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        while( op == tk_logor )
        {
            word wContinueLbl = 0;
            // gen test of first part of some logical expression and jump to wSkipLabel in case false
            if( bInCondExp ) // typical case will be: if( obj1 || obj2 )
            {
                wContinueLbl = get_NextLabel();
                gen_nz( Func, vResult ); // convert result to boolean -> t_s4
                gen_dup( Func );
                gen_ifnz( Func, vResult, wContinueLbl ); // if result is non-zero, we can skip all this level, but live the result on stack
            }

            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                iRet = exp_level3_logical_and( symList, vHold, bInCondExp, Func );
                if(  iRet == XORT_OK )
                {
                    iRet = exp_arith( symList, op, vResult, vHold, getNULLSymbol(), 0, Func );
                    if( iRet == XORT_OK ) {
                        op = g_Token.m_dwTokenCode;
                    } else {
                        break;                        
                    }
                } else
                    break;

                if( wContinueLbl ) {
                    gen_label( Func, wContinueLbl ); // continue evaluation at this point
                }
            } else
                break;
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// ? : conditional expression: expr ? expr1 : expr2
//---------------------------------------------------------------------------------------------
STATIC int exp_level1_conditional( SYMBOLLIST symList, SymbolRef vResult, SymbolRef vTargetResult, int bInCondExp, SymbolRef Func )
{
    int iRet; Symbol vHold1, vHold2; dword op;

    // calculate the primary condition expression
    iRet = exp_level2_logical_or( symList, vResult, bInCondExp, Func );
    if( iRet == XORT_OK )
    {
        op = g_Token.m_dwTokenCode;
        if( op == tk_quest )
        {
            word wExpr2label = get_NextLabel();
            gen_ifz( Func, vResult, wExpr2label ); // check expression result and goto expr2 if false
            // if result is true - fall into expr1 block
            iRet = exp_get_token( symList );
            if( iRet == XORT_OK )
            {
                // calculate expr1 - anyway -> syntax should be checked!
                iRet = exp_level2_logical_or( symList, vHold1, bInCondExp, Func );
                if( iRet == XORT_OK )
                {
                    if( !vTargetResult.isNULL() ) { // convert the result to target result type!
                        iRet = gen_convert( Func, vTargetResult, vHold1 );
                        if( iRet != XORT_OK )
                            return iRet;
                    }

                    word wSkipExpr2Label = get_NextLabel();
                    gen_goto( Func, wSkipExpr2Label ); // goto end of ternary construct

                    if( g_Token.m_dwTokenCode == tk_colon )
                    {
                        iRet = exp_get_token( symList );
                        if( iRet == XORT_OK )
                        {
                            gen_label( Func, wExpr2label ); // mark the start of expr2

                            // calculate expr2 - anyway -> syntax should be checked!
                            iRet = exp_level2_logical_or( symList, vHold2, bInCondExp, Func );
                            if( iRet == XORT_OK )
                            {
                                if( !vTargetResult.isNULL() ) { // convert the result to target result type!
                                    iRet = gen_convert( Func, vTargetResult, vHold2 );
                                    if( iRet != XORT_OK )
                                        return iRet;
                                }

                                gen_label( Func, wSkipExpr2Label ); // mark end of expr2
                                iRet = exp_arith( symList, op, vResult, vHold1, vHold2, 0, Func );
                            }
                        }
                    } else {
                        iRet = ERR_SYNTAX; // ':' is required!
                    }
                }
            }
        }
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------
// Entry point to the parser - only one exported function from this module!
//---------------------------------------------------------------------------------------------
int get_expr( SYMBOLLIST symList, SymbolRef vResult, SymbolRef Func, SymbolRef vTargetResult, int bInCondExp )
{
    int iRet;
    vResult.Zero();

    iRet = exp_get_token( symList );

    if( iRet == XORT_OK )
    {
        iRet = exp_level1_conditional( symList, vResult, vTargetResult, bInCondExp, Func );
        if( iRet != XORT_OK ) {
            x_error( ERR_EXPR_SYNTAX, g_Token.m_dwLine, g_Token.m_dwPos );
        }
    }

    g_bTokenGot = FALSE;

    return iRet;
}
//---------------------------------------------------------------------------------------------
// entry point to parse obj.member1[.member2]* = 
//---------------------------------------------------------------------------------------------
int exp_mem_ptr( SYMBOLLIST symList, SymbolRef vResult, SymbolRef pFunc, SymbolRef Var )
{
    int iRet; Symbol vHold; dword op;

    Var.Copy( vResult );
    iRet = gen_load( pFunc, vResult );
    if( iRet != XORT_OK ) {
        return iRet;
    }

    op = tk_ptr;
    while( op == tk_ptr )
    {
        iRet = exp_get_token( symList );
        if( iRet == XORT_OK )
        {
            iRet = exp_primary( symList, vHold, vResult, pFunc );
            if( iRet == XORT_OK )
            {
                Symbol nullSym; nullSym.Zero(); // we can not pass NULL to the reference argument, so nullSymbol is the replacement for NULL
                iRet = exp_arith( symList, op, vResult, vHold, nullSym, 0, pFunc );
                if( iRet == XORT_OK ) {
                    op = g_Token.m_dwTokenCode;
                    vHold.Copy( vResult );
                    vHold.Zero();
                } else {
                    break;
                }
            } else
                break;
        } else
            break;
    }

    return iRet;
}
//---------------------------------------------------------------------------------------------